using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Windows.Threading;

namespace AGameOfThrones.network
{
    public class connection
    {
        Thread m_thread;
        TcpClient m_tcpclient;

        public Thread Thread
        {
            get { return m_thread; }
        }

        public TcpClient TcpClient 
        { 
            get { return m_tcpclient; }
        }

        public connection(TcpClient in_tcpClient)
        {
            m_tcpclient = in_tcpClient;
        }   

        void threadmain()
        {
            byte[] receiveBuffer = new byte[1024 * 1024];
           
            DateTime lastDataReceived = DateTime.Now;
            TimeSpan timeOutSpan = new TimeSpan(0, 0, 15);
            while (m_tcpclient.Connected)
            {
                try
                {
                    if (m_tcpclient.GetStream().DataAvailable)
                    {
                        int bytesRead = m_tcpclient.GetStream().Read(receiveBuffer, 0, receiveBuffer.Length);
                        if (bytesRead > 0)
                        {
                            byte[] buffer = new byte[bytesRead];
                            Array.Copy(receiveBuffer, 0, buffer, 0, bytesRead);
                            RecieveData(buffer, bytesRead);
                            Console.WriteLine("Read Data" + bytesRead);
                            lastDataReceived = DateTime.Now;
                        }
                    }
                    else
                    {
                        Thread.Sleep(100);

                        /*
                        if (DateTime.Now - lastDataReceived > timeOutSpan)
                        {
                            System.Diagnostics.Debug.WriteLine("Client time out");
                            m_tcpclient.Close();
                        }*/
                    }
                }
                catch (SocketException e)
                {
                    Thread.Sleep(100);
                    System.Diagnostics.Debug.WriteLine(e.ToString());
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine(e.ToString());
                }
            }
            try
            {
                m_tcpclient.Close();  
            }
            catch (System.Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e.ToString());
            }
        }

        public void Start()
        {
            m_thread = new Thread(new ThreadStart(threadmain));
            m_thread.Priority = ThreadPriority.BelowNormal;
            m_thread.Start();
            if (OnNetworkConnected != null)
            {
                OnNetworkConnected(this, new OnNetworkConnectedEvent());

            }
        }

        public void Abort()
        {
            if (m_tcpclient.Connected)
            {
                m_tcpclient.Client.Close();
                m_tcpclient.Close();
                m_thread.Abort();
                m_thread.Join();
                if (OnNetworkDisConnected != null)
                {
                    OnNetworkDisConnected(this, new OnNetworkDisConnectedEvent());
                }
            }
        }

        public void SendData(byte[] buffer, int bufflen)
        {
            try
            {
                m_tcpclient.GetStream().Write(buffer, 0, bufflen);
            }
            catch(Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e.ToString());
            }
            
        }

        public void RecieveData(byte[] buffer, int bufflen)
        {
            MemoryStream ms = new MemoryStream(buffer, 0, bufflen);
            messages.factory f = new messages.factory();
            stream.istream_xml s = new stream.istream_xml(f);
            messages.packet packet = new messages.packet();
            s.open(ms);
            packet.serialize(s);
            s.close();
            ms.Close();
            RecieveMessage(packet.Data as messages.message);
        }

        public void RecieveMessage(messages.message message)
        {
            if (OnNetworkData != null)
            {
                OnNetworkData(this, new OnNetworkDataEvent(message));
            }
        }

        public void SendMessage(messages.message message)
        {
            messages.packet packet = new messages.packet();
            packet.Data = message;
            MemoryStream ms = new MemoryStream();
            stream.ostream_xml s = new stream.ostream_xml();
            s.open(ms);
            packet.serialize(s);
            s.close();
            byte[] bytes = ms.ToArray();         
            SendData(bytes, bytes.Length);
            ms.Close();
        }
                
        public class OnNetworkDataEvent : EventArgs
        {
            public OnNetworkDataEvent(messages.message in_message)
            {
                Message = in_message;
            }

            public messages.message Message
            {
                get;
                set;
            }
        };


        public class OnNetworkConnectedEvent : EventArgs
        {
            public OnNetworkConnectedEvent()
            {
            }
        };

        public class OnNetworkDisConnectedEvent : EventArgs
        {
            public OnNetworkDisConnectedEvent()
            {
            }
        };

        public delegate void OnNetworkDataDelegate(object sender, OnNetworkDataEvent e);
        public delegate void OnNetworkConnectedDelegate(object sender, OnNetworkConnectedEvent e);
        public delegate void OnNetworkDisConnectedDelegate(object sender, OnNetworkDisConnectedEvent e);

        public event OnNetworkDataDelegate OnNetworkData;
        public event OnNetworkConnectedDelegate OnNetworkConnected;
        public event OnNetworkDisConnectedDelegate OnNetworkDisConnected;

    }
}
